/**
 * document.getElementById のシンタックスシュガー
 * @param {String} aID
 * @return {Node}
 */
function $(aID){
	return document.getElementById(aID);
}

/**
 * uidからノードを取得する
 * @param {String} uid
 */
function $U(uid){
	return uuQuery('[uid="' + uid + '"]')[0];
}

/**
 * document.getElementsByClassName のシンタックスシュガー<br>
 * 該当ノードが1つしかない場合はノードそのものを返す
 *
 * @param {String} aClass クラス名
 * @param {Node} [aTarget=":root"] 捜索範囲
 * @return {Node|ElementArray}
 */
function $C(aClass, aTarget){
	var result = uuQuery.className(aClass, aTarget);
	return result.length === 1 ? result[0] : result;
}

/**
 * XPathで要素を得る
 *
 * @param {String} aExpression XPath式
 * @param {Node} [aParent=":root"] 捜索範囲
 * @return {ElementArray}
 */
function $X(aExpression, aParent) {
	var r = [];
	var x = document.evaluate(aExpression, aParent || document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
	for (var i=0,l=x.snapshotLength;i<l;i++) {
		r.push(x.snapshotItem(i));
	}
	return r;
}


/** @ignore */
window.xconfig = { storage: 1 * 1000 * 1000 }; // 1MB

/** @ignore */
window.xstorage = function(uu, storage){
	joCore.init();
}


/**
 * 基本的な機能を提供する
 * @class
 */
var joCore = {

	/**
	 * 起動時に実行され, 各オブジェクトの初期化を行う
	 * @private
	 */
	init: function(){
		Deferred.next(function(){
			return joCore._load();
		}).
		next(function(){
			return joPrefManager.init();
		}).
		next(function(){
			return joFileManager.init();
		}).
		next(function(){
			return joEvent.init();
		}).
		next(function(){
			return joAppManager.init();
		}).
		next(function(){
			joCore.aboutOS();
		}).
		next(function(){
			return joCore.IO.load('js', 'Libraries/xcss.js');
		}).
		error(function(e){
			joCore.logger.log('Booting Error!');
			joCore.logger.error(e);
		});
	},

	/**
	 * 終了時に実行され, 各オブジェクトの終了処理を行う
	 * @private
	 */
	uninit: function(){
		joAppManager.uninit();
		joPrefManager.uninit();
	},
	
	/**
	 * OSを構成するファイルを読み込む<br>
	 * 起動時に一度だけ実行される
	 */
	_load: function(){
		return Deferred.parallel([
			joCore.IO.load('js', 'Libraries/json2.js'),
			joCore.IO.load('js', 'Libraries/uki.js'),
			joCore.IO.load('js', 'Libraries/uki-more.js'),
		]).next(function(){
			return Deferred.parallel([
				joCore.IO.load('js', 'contextmenu.js'),
				joCore.IO.load('js', 'ui.js'),
				joCore.IO.load('js', 'window.js'),
				joCore.IO.load('js', 'filesystem.js'),
				joCore.IO.load('js', 'event.js'),
				joCore.IO.load('js', 'application.js'),
				joCore.IO.load('js', 'prefs.js'),
			]);	
		});
	},


	/**
	 * OSについての情報を表示する<br>
	 * About OS... から呼び出される
	 */
	aboutOS: function(){
		var message = [
			'Dinos ver 0.0.1',
			'Created by nodaguti',
			'',
			'Dinos stands for "Dinos Is Not OS."',
			'It written in JavaScript.',
			'',
			'<a href="javascript:joCore.showChangeLog();">Change Log</a>',
			'<a href="javascript:joCore.showThanks();">Thanks</a>',
			'<hr>',
			'<h4>Experiments</h4>',
			'<a href="javascript:void(joCore.UI.prompt({message: \'File Name\'}).next(function(name){joCore.IO.Local.read(name, \'Text\').next(function(str){alert(str);})}).error(function(e){joCore.logger.error(e);}));">FileReader</a>',
			'<a href="javascript:void(joCore.UI.prompt({message: \'Mode\'}).next(function(mode){(new joFilePicker({mode:mode})).show()}));">FilePicker</a>',
		].join('<br />');

		joCore.UI.dialog({
			width: 500,
			height: 300,
			message: message,
			application: 'System',
			title: 'About This OS'
		});
	},

	/**
	 * ChangeLog.txt を表示する<br>
	 * About OS... から呼び出される
	 */
	showChangeLog: function(){
		this.IO.read('ChangeLog.txt', 'text/plain').next(function(log){
			joCore.UI.dialog({
				width: 500,
				height: 300,
				message: joCore.textToHTML(log.responseText),
				application: 'System',
				title: 'Change Log'
			});
		});
	},

	/**
	 * Thanks.txt を表示する<br>
	 * About OS... から呼び出される
	 */
	showThanks: function(){
		this.IO.read('Thanks.txt', 'text/plain').next(function(log){
			joCore.UI.dialog({
				width: 500,
				height: 300,
				message: joCore.textToHTML(log.responseText),
				application: 'System',
				title: 'Change Log'
			});
		});	
	},
	
	/**
	 * Plain Text to innerHTML
	 * innerHTMLでテキストをそのまま流し込むときにどうぞ
	 * @param {String} text
	 * @param {Boolean} [strict=false] 厳格モードにするかどうか <br>trueにするとタグを無効化する
	 * @param {Number} [tablen=3] タブ幅
	 * @return {String}
	 */
	textToHTML: function(text, strict, tablen){
		var tab = '', tablen = tablen ? tablen : 3;
		for(var i=1;i<tablen;i++) tab += '&nbsp;';
	
		text = text.replace(/&/g, '&amp;')
		           .replace(/ /g, '&nbsp;')
		           .replace(/\n/g, '<br />')
		           .replace(/\t/g, tab);
		
		if(strict){
			text = text.replace(/</g, '&lt;')
			           .replace(/>/g, '&gt;');
		}
		
		return text;
	},
	
	/**
	 * make Array-like Object Array
	 * @param {Object} object
	 * @return {Array}
	 */
	toArray: function(object){
		return Array.prototype.slice.call(object);
	},
	
	getUniqueID: function(){
		return UUID.genV4().toString();
	},
};


/**
 * Useful methods for Element
 * @class
 */
joCore.Node = {

	/**
	 * 要素を作成する
	 *
	 * @param {String} aTagName 作成する要素の名前
	 * @param {Object} [attributes] 要素に追加する属性 <br>{ attributeName: attributeValue, ... }
	 * @param {Node} [aOwner] 要素の親
	 * @param {String} [aNameSpace] 要素の名前空間 <br>省略時は document.craeteElement と等価
	 */
	createNode: function(aTagName, attributes, aOwner, aNameSpace){
		var tag = (aNameSpace === undefined) ? document.createElement(aTagName) :
		                                       document.createElementNS(aNameSpace, aTagName);

		if(attributes) this.setAttributes(tag, attributes);
		if(aOwner) aOwner.appendChild(tag);
		
		this.getUniqueID(tag);

		return tag;
	},
	
	/**
	 * 各要素に固有のIDを返す
	 * @param {Node} aElement
	 * @return {String} id
	 */
	getUniqueID: function(aElement){
		if(typeof aElement.hasAttribute === 'undefined') return;
		if(aElement.hasAttribute('uid')){
			return aElement.getAttribute('uid');
		}else{
			var id = joCore.getUniqueID();
			aElement.setAttribute('uid', id);
			return id;
		}
	},
	
	/**
	 * 指定した座標にあるノードをすべて取得する
	 *
	 * @param {Number} x x座標
	 * @param {Number} y y座標
	 * @return {ElementArray} 一番上 -> 一番下の順でソート済み
	 */
	getNodesByPosition: function(x, y){
		var result = [];  //該当する要素の配列
		var element;
		
		do{
			element = document.elementFromPoint(x, y);
			result.push(element);
			joCore.Node.addStyle(element, { display: 'none' });
		}while(element.tagName.toLowerCase() !== 'html');
		
		result.forEach(function(el){ joCore.Node.removeStyle(el, { display: 'none' }); });
		
		return result;
	},
	
	/**
	 * 要素を移動する
	 *
	 * @param {Node} aElement 対象の要素
	 * @param {Node} moveTo 移動先の親要素
	 */
	moveNode: function(aElement, moveTo){
		aElement.parentNode.removeChild(aElement);
		moveTo.appendChild(aElement);
	},

	/**
	 * 指定した要素を削除する
	 *
	 * @param {Node}
	 */
	remove: function(aElement){
		aElement.parentNode.removeChild(aElement);
	},
	
	/**
	 * 指定したCSS Selectorに合致する要素を削除する
	 *
	 * @param {String} aSelector CSS Selector
	 */
	deleteNodes: function(aSelector){
		var elements = uuQuery(aSelector);

		for(var i=0,l=elements.length;i<l;i++){
			elements[i].parentNode.removeChild(elements[i]);
		}

		elements = null;
	},

	/**
	 * 要素に属性を追加する
	 *
	 * @param {Node} aElement 対象の要素
	 * @param {Object} attributes 追加する属性 <br>{ attributeName: attributeValue, ... } <br>
	 *                            属性名を :context にすると、要素にテキストノードを追加する
	 */
	setAttributes: function(aElement, attributes){
		for(i in attributes){
			if(i === ':context') //テキストノードの場合
				aElement.appendChild( document.createTextNode(attributes[i]) );
			else if(i === ':child') //innerHTML
				aElement.innerHTML += typeof attributes[i] === 'string' ? attributes[i] : attributes[i].innerHTML;
			else
				aElement.setAttribute(i, attributes[i]);
		}
	},

	/**
	 * 要素にスタイルを追加する<br>
	 * style属性に追加されるので優先順位に注意すること
	 *
	 * @param {Node} aElement 対象の要素
	 * @param {Object} aSelectors 追加するスタイル <br>{ Property: Value, ... }
	 */
	addStyle: function(aElement, aSelectors){
		var selectorStr = '';

		for(i in aSelectors){
			selectorStr += i + ':' + aSelectors[i] + ';';
		}
		
		var style = aElement.getAttribute('style');
		if(style && style.indexOf(';') !== (style.length-1)) style = style + ';';
		if(style) selectorStr = style + selectorStr;
		aElement.setAttribute('style', selectorStr);
	},
	
	/**
	 * 要素のスタイルを削除する
	 */
	removeStyle: function(aElement, aSelectors){
		var style = aElement.getAttribute('style');
		if(!style) return;

		for(i in aSelectors){
			var r = new RegExp(i + '\\s*:\\s*' + aSelectors[i] + '\\s*;\\s*', 'g');
			style = style.replace(r, '');
		}
		
		aElement.setAttribute('style', style);
	},

	/**
	 * 指定したクラス名を追加する
	 *
	 * @param {Node} aElement 対象の要素
	 * @param {String|Array} aClassNames 追加するクラス名<br>
	 *                                   複数一括追加をする場合は配列か半角スペース区切りの文字列を渡す<br>
	 *                                   すでにクラス名が存在していた場合は何も行わない
	 */
	addClass: function(aElement, aClassNames){
		var classes = typeof aClassNames === 'object' ? aClassNames : aClassNames.split(' ');
		
		for(var i=0,l=classes.length;i<l;i++){
			if(this.hasClass(aElement, classes[i])) continue;
		
			aElement.className += ' ' + classes[i];
		}
	},

	/**
	 * 指定したクラス名を削除する
	 *
	 * @param {Node} aElement 対象の要素
	 * @param {String|Array} aClassNames 削除するクラス名<br>
	 *                                   複数一括削除をする場合は配列か半角スペース区切りの文字列を渡す
	 */
	removeClass: function(aElement, aClassNames){
		var classes = typeof aClassNames === 'object' ? aClassNames : aClassNames.split(' ');
		
		for(var i=0,l=classes.length;i<l;i++){
			if(!this.hasClass(aElement, classes[i])) continue;
		
			aElement.className = aElement.className.replace(classes[i], '');
		};
	},

	/**
	 * 指定したクラス名を持っているかどうか調べる
	 *
	 * @param {Node} aElement 対象の要素
	 * @param {String} aClassName クラス名
	 * @return {Boolean}
	 */
	hasClass: function(aElement, aClassName){
		var c = aElement.getAttribute('class');
		return c && c.indexOf(aClassName) !== -1;
	},

	/**
	 * String -> DOM
	 *
	 * @param {String} aString DOMへ変換する文字列
	 * @param {String} [aMimeType="text/xml"] MimeType
	 * @return {Node}
	 */
	textToDOM: function(aString, aMimeType){
		try{
			var parser = new DOMParser();
			return parser.parseFromString(aString, aMimeType || 'text/xml');
		}catch(e){
			try{
				var range = document.createRange();
				range.selectNodeContents(document.body);  //Hack for Safari, Opera
				return range.createContextualFragment(aText);
			}catch(ex){}
		}
		
		return null;
	},

	/**
	 * DOM -> String
	 *
	 * @param {Node} aDOM
	 * @return {String}
	 */
	DOMToText: function(aDOM){
		var serializer = new XMLSerializer();
		return serializer.serializeToString(aDOM);
	},

	/**
	 * ノードの位置情報を得る
	 *
	 * @param {Node} aElement 情報を取得する要素
	 * @param {Node} [aParent] 位置の基準となる要素<br>
     *                         省略時は Element.getBoundingClientRect() と同義になる
	 * @return {Object} ノードの位置情報を持つオブジェクト
	 * @return {Number} .left 左端からの距離 [px]
	 * @return {Number} .right 右端からの距離 [px]
	 * @return {Number} .top 上端からの距離 [px]
	 * @return {Number} .bottom 下端からの距離 [px]
	 * @return {Number} .width 横幅 [px]
	 * @return {Number} .height 高さ [px]
	 */
	getNodeRect: function(aElement, aParent){
		var elementRect = aElement.getBoundingClientRect();
		
		if(aParent){
			var parentRect = aParent.getBoundingClientRect();
		
			return {
				left: elementRect.left - parentRect.left,
				right: elementRect.right - parentRect.right,
				top: elementRect.top - parentRect.top,
				bottom: elementRect.bottom - parentRect.bottom,
				width: elementRect.right - elementRect.left,
				height: elementRect.bottom - elementRect.top
			};
		}else{
			return {
				left: elementRect.left,
				right: elementRect.right,
				top: elementRect.top,
				bottom: elementRect.bottom,
				width: elementRect.right - elementRect.left,
				height: elementRect.bottom - elementRect.top
			};
		}
	},
	
	/**
	 * フォーム関連要素の値を得る
	 * @param {Node} aElement input, textarea, select, or radiogroup(element[class=radiogroup] which has a group of input[type=radio])
	 * @param {Boolean} [getValue=false] whether returns aElement.value or not(returns aElement.checked)
	 * @return {String|Array} select[multiple=true] might return Array.
	 */
	getFormValue: function(aElement, getValue){
		switch(aElement.tagName.toLowerCase()){
			case 'input':
				switch(aElement.type){
					case 'checkbox':
					case 'radio':
						return getValue ? (aElement.checked ? aElement.value : null) : aElement.checked;
					
					default:
						return aElement.value;						
				}
			
			case 'textarea':
				return aElement.value;
			
			case 'select':
				var options = joCore.toArray(aElement.options);
				var values = [];
				options.forEach(function(option){
					if(option.selected)
						this.push(option.value);			
				}, values);
				
				return values.length ? values : null;
			
			default:  //may be radiogroup
				var radios = uuQuery('input[type=radio]', aElement);
				var values = [];
				radios.forEach(function(radio){
					var v = joCore.getFormValue(radio);
					if(v !== null) this.push(v);				
				}, values);
				
				return values.length ? values : null;
		}
	},
	
	/**
	 * フォーム関連要素の値をセットする
	 * @param {Node} aElement input, textarea, select, or radiogroup(element[class=radiogroup] which has a group of input[type=radio])
	 * @param {String|Array} value the value(s) to set
	 */
	setFormValue: function(aElement, value){
		switch(aElement.tagName.toLowerCase()){
			case 'input':
				switch(aElement.type){
					case 'checkbox':
					case 'radio':
						aElement.checked = value;

					default:
						aElement.value = value;					
				}
			
			case 'textarea':
				aElement.value = value;
/*
			//not implemented			
			case 'select':
				var options = joCore.toArray(aElement.options);
				var values = [];
				options.forEach(function(option){
					if(option.selected)
						this.push(option.value);			
				}, values);
				
				return values.length ? values : null;
			
			default:  //may be radiogroup
				var radios = uuQuery('input[type=radio]', aElement);
				var values = [];
				radios.forEach(function(radio){
					var v = joCore.getFormValue(radio);
					if(v !== null) this.push(v);				
				}, values);
				
				return values.length ? values : null;*/
		}
	},
};
 
 
/**
 * IO service of Dinos File System and Local Files
 * @class
 */
joCore.IO = {

	/**
	 * ファイルを読み込む（DOM0）
	 *
	 * @param {String} aFileType js,css,無指定 のどれか
	 * @param {String} aFilePath core.js からみた相対パス
	 * @return {Deferred|Node} js: script要素 <br>
	 *                         css: link要素 <br>
	 *                         その他: iframe要素 が返る
	 */
	load: function(aFileType, aFilePath){
		var deferred = new Deferred();

		var tag = aFileType === 'js' ? joCore.Node.createNode('script', {
											type : 'text/javascript',
											src : aFilePath
										}, document.body) :
				  aFileType === 'css' ? joCore.Node.createNode('link', {
											type: 'text/css',
											href: aFilePath,
											rel: 'stylesheet'
										}, document.body) :
				  joCore.Node.createNode('iframe', {
				  							href: aFilePath,
				  							style: 'width:0;height:0;visibility:hidden'
				  						}, document.body);

		tag.addEventListener('load', function(){ deferred.call(tag); }, false);
		return deferred;
	},
	
	/**
	 * ファイルを読み込む（Ajax）
	 * 
	 * @param {String/DinosPath} aFilePath 読み込むファイル
	 * @param {String} [aMimeType="text/xml"] ファイルのMimeType
	 * @return {Deferred|XMLHttpRequest}
	 */
	read: function(aFilePath, aMimeType){
		try{
			var deferred = new Deferred();
			var req = new XMLHttpRequest();
			req.onerror = function(){ deferred.fail(req); };
			req.onreadystatechange = function(){
				if(req.readyState === 4){
					if(req.status === 0 || req.status === 200)
						deferred.call(req);
					else
						deferred.fail(req);
				}
			};
		
			req.open('GET', aFilePath, true);
			req.overrideMimeType(aMimeType || 'text/xml');
			req.send(null);
		}catch(e){
			setTimeout(function(){ deferred.fail(req); }, 0);
			return deferred;
		}
		
		return deferred;
	},

	/**
	 * ファイルに書き込む <br>
	 * もし当該ファイルが存在していなかった場合には自動で作成する
	 *
	 * @param {String} aString 書き込むデータ
	 * @param {String/DinosPath} aFilePath 書き込み先のファイル
	 * @param {Boolean} notCheckDuplicate 重複を警告するかどうか
	 * @return {Node} 書き込み先のファイルのノード
	 */
	write: function(aString, aFilePath, notCheckDuplicate){
		var fileName = joFileManager.getFileNameFromPath(aFilePath);
		var folderPath = joFileManager.getParentFolderFromPath(aFilePath);

		joCore.logger.log('write a file:', {file: fileName, folder: folderPath});

		if(joFileManager.exist(aFilePath)){
			if(!notCheckDuplicate){
				joCore.UI.confirm({
					message: fileName + 'という名前のファイルがすでにこの場所に存在しています。上書きしますか？'
				}).next(function(overwriteFlag){
					if(overwriteFlag) joFileManager.write(aString, aFilePath);
				});
			}else{
				joFileManager.write(aString, aFilePath);
			}
		}else{
			joFileManager.createFile(fileName, folderPath);
			joFileManager.write(aString, aFilePath);
		}
	},
	
	/**
	 * ローカルファイルを扱うクラス
	 */
	Local: {
	
		/**
		 * ファイルを書き出す
		 * @param {String} aString 書き出す文字列
		 * @param {String} aFileName ファイルネーム
		 */
		write: function(aString, aFileName){
			Deferred.next(function(){
				return joCore.UI.prompt({
					width: 300,
					height: 100,
					title: 'Save as...',
					message: 'Enter the file name.',
					defaultStr: aFileName,
				});		
			}).next(function(name){
				return joCore.UI.dialog({
					width: 300,
					height: 100,
					message: '<p>Click the below link <b>with alt-key</b> to save file.</p>' + 
							 '<a class="local-write-link" href="data:application/octet-stream,' + 
							 encodeURIComponent(aString) + '">' + name + '</a>',
					title: 'Save as...',
				});
			}).error(function(e){ joCore.logger.error(e); });
		},
	
		/**
		 * ファイルを読み込む
		 * @param {String} aFileName 読み込むファイル名
		 * @param {String} [readType=Text] 読み込むファイル形式。Text, Binary, DataURLを指定可能。
		 * @return {Deferred|String}
		 */
		read: function(aFileName, readType){
			return Deferred.next(function(){   //ダイアログを表示して、ユーザーがファイルを指定するのを待機する
				var deferred = new Deferred();
				
				//ダイアログ表示
				var dialog = joCore.UI.dialog({
					width: 300,
					height: 135,
					message: '<p>Dinos want to access a local file. <br /> \
							  Please select or drop "<b>' + aFileName + '</b>".</p><br /> \
							  <input type="file" class="local-read-input" /><br /><br /> \
							  Status: <div class="local-read-status"></div>',
					title: 'Open as...',
				});
				
				//イベント追加
				var status = $C('local-read-status', dialog.display.content);
				
				joEvent.addEvent(dialog.display.content, 'dragenter', function(e){
					status.innerHTML = 'Dragging...';
					e.preventDefault();
				}, false);
				
				joEvent.addEvent(dialog.display.content, 'dragover', function(e){
					status.innerHTML = 'Dragging...';
					e.preventDefault();
				}, false);
				
				joEvent.addEvent(dialog.display.content, 'drop', function(e){
					e.preventDefault();
					status.innerHTML = 'File Dropped.';
					
					if(checkValidFile(e.dataTransfer.files)){
						dialog.close();
						deferred.call(e.dataTransfer.files);
					}else
						status.innerHTML = 'Invalid File Dropped.';
				}, false);
				
				joEvent.addEvent($C('local-read-input', dialog.display.content), 'change', function(){
					status.innerHTML = 'File Selected.';
					
					if(checkValidFile(this.files)){
						dialog.close();
						deferred.call(this.files);
					}else
						status.innerHTML = 'Invalid File Selected.';
				}, false);
				
				//要求されたファイルが指定されたのかどうかを調べる
				function checkValidFile(files){
					var file = files[0];
					if(!file.name || !file.fileName) return false;
					return (file.name && file.name.indexOf(aFileName) > -1) || (file.fileName && file.fileName.indexOf(aFileName) > -1);
				}
			
				return deferred;
			}).next(function(files){   //ファイルの中身を読んで返す
				var deferred = new Deferred();
				var reader = new FileReader();
				var file = files[0];
				
				//読み込み完了時に待機を解除
				reader.onload = (function(def){ return function(e){ def.call(e.target.result); } })(deferred);
				reader.onerror = (function(def){ return function(e){ joCore.logger.error(e.target.error); def.fail(); } })(deferred);
			
				//読み込み
				switch(readType){
					case 'Text':
						reader.readAsText(file);
						break;
						
					case 'Binary':
						reader.readAsBinary(file);
						break;
						
					case 'DataURL':
						reader.readAsDataURL(file);
						break;
				}
				
				return deferred;
			});
		},
	
	},
};


/**
 * Logger
 * @class
 */
joCore.logger = {

	/**
	 * ログを取る<br>
	 * console.log, window.dump, alertの順に試行する
	 *
	 * @param {All} mes 複数の引数を指定した場合は半角スペースで区切って出力する
	 */
	log: function(mes){
		if(window.console){
			console.log.apply(console, arguments);
		}else if(window.dump){
			window.dump((joCore.toArray(arguments)).join(' ') + '\n');
		}else{
			alert((joCore.toArray(arguments)).join('\n'));
		}
	},

	/**
	 * エラーを出力する<br>
	 * Errorオブジェクトを渡した場合は, Errorオブジェクトが持つ全てのプロパティを出力する
	 *
	 * @param {Error} er エラーオブジェクト
	 */
	error: function(er){
		this.log('[ERROR] ', er.name);
		for(i in er) this.log(i, ':', er[i]);
	},
	
	/**
	 * タイマーオブジェクト
	 * @class
	 */
	timer: {
	
		/**
		 * タイマーをスタートさせる
		 * @return {Number} タイマーID <br> 実体はDate.getTime()の値
		 */
		start: function(){
			return (new Date()).getTime();
		},
		
		/**
		 * タイマーを終了させる <br> 結果はjoCore.logger.logで出力される
		 * @param {Number} aID タイマーID
		 */
		end: function(aID){
			var end = (new Date()).getTime();
			joCore.logger.log('[joCore timer]', end - aID, 'ms');
		},
		
	},
};


/**
 * joDisplay
 * @class
 */
var joDisplay = {

	init: function(){
		this.setOffset(0, joCore.Node.getNodeRect($('menubar')).height);

		joEvent.addEvent(window, 'resize', this.onResize, false, this);
		this.onResize();
	},

	/** @function */
	get displayWidth(){
		return document.documentElement.clientWidth - this._offsetX;
	},

	/** @function */
	get displayHeight(){
		return document.documentElement.clientHeight - this._offsetY;
	},

	/**
	 * ディスプレイのオフセットを設定する
	 * @param {Number} x 上端
	 * @param {Number} y 左端
	 */
	setOffset: function(x, y){
		this._offsetX = x;
		this._offsetY = y;
	},

	/**
	 * ウィンドウがリサイズされたときに呼び出される 
	 */
	onResize: function(){
		joCore.Node.addStyle($('windows'), {
			'top': this._offsetY + 'px',
			left: this._offsetX + 'px',
			width: this.displayWidth + 'px',
			height: this.displayHeight + 'px'
		});
	}

};
